package lifecycle;

import domain.SuperUser;
import domain.User;
import org.junit.Test;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author jackcfan
 * @date 2021/9/26
 */
public class BeanInstantiationTest {

    /**
     * BeanFactory 不会回调6个特殊 aware 接口
     * 例如 {@link User#setEnvironment(Environment)}
     * */
    @Test
    public void testBeanFactory() {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 添加 BeanPostProcessor
        beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());

        XmlBeanDefinitionReader definitionReader = new XmlBeanDefinitionReader(beanFactory);
        definitionReader.loadBeanDefinitions("bean-context.xml");

        SuperUser superUser = beanFactory.getBean("superUser", SuperUser.class);
        User user = beanFactory.getBean("user", User.class);

        System.out.println("user>"+user);
        System.out.println("superUser>"+superUser);

    }

    /**
     * 只有使用 applicationContext 时才会回调以下6个 aware 接口
     * EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、
     * ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
     * */
    @Test
    public void testApplicationContext() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean-context.xml");

        SuperUser superUser = applicationContext.getBean("superUser", SuperUser.class);
        User user = applicationContext.getBean("user", User.class);

        System.out.println("user>"+user);
        System.out.println("superUser>"+superUser);

        applicationContext.close();
    }

    static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{

        /**
         * 实例化之前执行，可以用来替换 Spring IoC 里的默认实现
         * @see AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
         * */
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            // 如果是超级用户，则新创建对象覆盖掉
            if("superUser".equals(beanName) && SuperUser.class.equals(beanClass)){
                SuperUser superUser = new SuperUser();
                superUser.setAddress("火星");
                superUser.setId(1024l);
                return superUser;
            }
            // 其他类型不处理
            return null;
        }

        /**
         * {@link AbstractAutowireCapableBeanFactory#populateBean(String, RootBeanDefinition, BeanWrapper)}
         * */
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            // 如果返回 false，则不允许对该 Bean 属性赋值
            /*if("user".equals(beanName)){
                // user 所有属性未赋值
                User user = (User) bean;
                user.setName("手动赋值，覆盖xml配置");
                return false;
            }*/
            return true;
        }

        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            // 对 user 属性进行拦截
            if("user".equals(beanName) && User.class.equals(bean.getClass())){
                final MutablePropertyValues propertyValues;

                if(pvs instanceof MutablePropertyValues){
                    propertyValues = (MutablePropertyValues) pvs;
                }else{
                    // 添加原属性
                    propertyValues = new MutablePropertyValues(pvs);
                }

                // 覆盖属性，等价于 <property name="name" value="spring"/>
                propertyValues.add("name","java");
                return propertyValues;
            }
            return null;
        }
    }
}
